(function() {
    var UTIL = Ext.util, WINDOW = window;
    
    /**
     * @class Ext.util.AjaxTaskManager 
     * @extends Object
     * <p>Ajax任务管理器, 合并多个Ajax请求到一个AjaxTask中, 一次请求一次返回</p>
     * <p>使用AjaxTask之前, 需要调用{@link #add}新建一个任务线程.<pre><code>
Ext.AjaxTask.add({
    id: 'newtask', // 任务ID
    url: '/ajaxtask.action' // 请求服务url
});
     * </code></pre></p>
     * 
     * <p>然后和通常一样调用{@link Ext.Ajax#request Ext.Ajax.request()}进行Ajax请求.</p>
     * <p>如果想将页面中的某个Ajax请求加入到AjaxTask中, 只需要在{@link Ext.Ajax#request request}参数中标识出相应的AjaxTask任务ID.<pre><code>
Ext.Ajax.request({
    task: 'newtask', // 任务ID
    uri: 'adduser', // 服务端资源
    success: function(response, options) {
        // 当AjaxTask返回服务端响应后, 统一执行回调函数
    }
});
     * </code></pre></p>
     * <p><b>Note</b>: 如果使用同步AJAX请求, 则不会被增加到任务线程中, 同步Ajax请求使用方法参见{@link Ext.Ajax#request request} async配置参数</p>
     * 
     * <p>{@link Ext.data.Store}同样也可以使用AjaxTask, 只需要在创建Store时, 标识出相应的AjaxTask任务ID.</p>
     * <p><b>proxy</b>必须使用{@link Ext.data.HttpProxy}<pre><code>
var proxy = new Ext.data.HttpProxy({
    api: {
        read: 'loaduser', // 服务端资源
        create: '/cext/user?method=create',
        update: '/cext/user?method=update',
        destroy: '/cext/user?method=remove'
    }
});

var store = new Ext.data.Store({
    task: 'test', // 任务ID
    taskAction: { // 声明哪类请求需要增加到AjaxTask中, 默认只有read请求才加入AjaxTask
        read: true, // True表示加入到AjaxTask中
        create: false, // False表示不加入AjaxTask
        update: false,
        destroy: false,
    },
    proxy: proxy,
    reader: reader,
    writer: writer
});
     * </code></pre></p>
     * 
     * <p>使用AjaxTask, 同时还要在服务端, 提供相应的处理程序, 目前只提供Java支持.</p>
     * 
     * <p>你需要提供一个服务代理类, 例如一个Servlet或Action:<pre><code>
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    response.setContentType("application/json");
    PrintWriter out = response.getWriter();
    String data = request.getParameter("data");
    // AjaxTaskImpl继承需要实现两个接口, parseParams和processRequest
    // parseParams : 解析请求参数
    // processRequest : 处理每个Ajax请求
    AjaxTask<String, String> ajaxTask = new AjaxTaskImpl();
    out.print(ajaxTask.process(data));
}
     * </code></pre></p>
     * <p>详细的使用方法, 请参见<a href="../examples/ajax/ajaxtask.html">Ajax Task Examples</a></p>
     *  
     * <p><b>Note</b>: 需要注意, 只有400的response.status状态码AjaxTask才能被正确处理, 如果AjaxTask请求失败, 那么Task中所有的子Ajax请求都将请求失败.</p>
     */
    UTIL.AjaxTaskManager = function(config) {
        config = config || {};
        Ext.apply(this, config);
        
        /**
         * AJAX任务线程 (只读)
         * @property tasks
         * @type Object
         */
        this.tasks = {};
    };

    Ext.extend(UTIL.AjaxTaskManager, Object, {
        /**
         * @cfg {String} method (Optional) AjaxTask请求HTTP method (defaults to 'POST')
         */
        method: 'POST',
        
        /**
         * @cfg {String} timeout (Optional) AjaxTask请求超时时间 (defaults to 30000)
         */
        timeout: 30000,
        
        /**
         * @cfg {String} header (Optional) AjaxTask请求头 (defaults to 'ExtPlus AjaxTask')
         */
        header: 'ExtPlus AjaxTask',
        
        /**
         * 添加一个AjaxTask到管理器中
         * @param {Object} taskCfg AjaxTask配置参数
         * <ul>
         * <li><b>id</b> : String <div class="sub-desc">AjaxTask唯一标识ID</div></li>
         * <li><b>uri</b> : String <div class="sub-desc">AjaxTask服务端资源uri</div></li>
         * <li><b>interval</b> : Number <div class="sub-desc">(Optional) 轮询间隔时间，如果有值并且interval>0，则为轮询任务</div></li>
         * <li><b>dataProperty</b> : String <div class="sub-desc">(Optional) 提交数据参数名, 在服务端使用这个名称获取数据 (defaults to 'data')</div></li>
         * <pre><code>
    String data = request.getParameter("data");
         * </code></pre>
         * </ul>
         * @param {Object} request (Optional) Ajax请求, 允许在添加AjaxTask时, 直接添加Ajax请求, 配置参数请参见{@link #addRequest addRequest}
         * @param {Boolean} execute (Optional) True添加AjaxTask之后立即执行
         * @return {Object} AjaxTask对象
         */
        add: function(taskCfg, request, execute) {
            taskCfg = taskCfg || {};

            if (!taskCfg.url) {
                return;
            }

            if (!taskCfg.interval || (taskCfg.interval <= 0)) {
                this.tasks[taskCfg.id] = new SimpleTask({
                    url: taskCfg.url,
                    manager: this
                });
            } else {
                this.tasks[taskCfg.id] = new PollingTask({
                    url: taskCfg.url,
                    interval: taskCfg.interval,
                    manager: this
                });
            }

            if (request) {
                if (Ext.isArray(request)) {
                    Ext.each(request, function(req) {
                        this.addRequest(this.tasks[taskCfg.id], req);
                    }, this);
                } else {
                    this.addRequest(this.tasks[taskCfg.id], request);
                }
            }

            if (execute) {
                this.start(this.tasks[taskCfg.id]);
            }

            return this.tasks[taskCfg.id];
        },

        /**
         * 获取一个AjaxTask
         * @param {String} task AjaxTask唯一ID
         * @return {Object} AjaxTask对象
         */
        get: function(task) {
            task = task || '';
            if (Ext.isObject(task)) {
                return task;
            } else if (Ext.isString(task)) {
                return this.tasks[task];
            }
            return null;
        },

        /**
         * 移除一个AjaxTask
         * @param {String/Object} task AjaxTask唯一ID, 或一个AjaxTask对象
         */
        remove: function(task) {
            if (task = this.get(task)) {
                if (task.isLocked()) {
                    task.removed = true;
                } else {
                    task.destroy();
                }
            }
        },

        /**
         * 移除所有的AjaxTask
         */
        removeAll: function() {
            if (this.tasks) {
                for (var i = 0; i < this.tasks.length; i++) {
                    this.remove(this.tasks[i]);
                }
            }
        },
        
        /**
         * 添加一个Ajax请求到AjaxTask中
         * @param {String/Object} task AjaxTask唯一ID, 或一个AjaxTask对象
         * @param {Object} requestConfig Ajax请求配置参数
         * <ul>
         * <li><b>uri</b> : String <div class="sub-desc">服务端资源uri, 在同一个AjaxTask中必须唯一</div></li>
         * <li><b>callback</b> : Function <div class="sub-desc">Ajax请求回调函数</div></li>
         * <li><b>params</b> : Object <div class="sub-desc">请求参数</div></li>
         * <li><b>options</b> : Object <div class="sub-desc">(Optional) Ajax请求的回调函数可选参数</div></li>
         * </ul>
         */
        addRequest: function(task, reqCfg) {
            if ((task = this.get(task)) && reqCfg) {
                if (!task.requests[reqCfg.uri] && task.removed !== true) {
                    task.requests[reqCfg.uri] = reqCfg;                 
                }
            }
        },

        /**
         * 移除一个AjaxTask中的Ajax请求
         * @param {String/Object} task AjaxTask唯一ID, 或一个AjaxTask对象
         * @param {String} uri 服务端资源uri, 在同一个AjaxTask中必须唯一
         */
        removeRequest: function(task, uri) {
            if ((task = this.get(task)) && uri) {
                if (task.removed === true) {
                    return;                    
                }
                if (task.isLocked()) {
                    task.removed.push(uri);
                } else {
                    delete task.requests[uri];
                }
            }
        },

        /**
         * 执行AjaxTask
         * @param {String/Object} task AjaxTask唯一ID, 或一个AjaxTask对象
         */
        start: function(task) {
            if (task = this.get(task)) {
                if (task instanceof SimpleTask) {
                    task.execute();
                } else if (task instanceof PollingTask) {
                    task.start();
                }
            }
        },

        /**
         * 中止AjaxTask
         * @param {String/Object} task AjaxTask唯一ID, 或一个AjaxTask对象
         */
        stop: function(task) {
            if (task = this.get(task)) {
                if (task instanceof PollingTask) {
                    task.stop();
                }
            }
        }
    });

    var handleTask = function(task) {
        if (task.isLocked() && task.removed !== true) {
            task.lock();

            var options = {
                header: task.manager.header,
                method: task.manager.method,
                url: task.url,
                params: {}
            }, requests = task.requests, params = [], callback, p;

            // 销毁已被删除task
            var removeFromBuffer = function() {
                if (task.removed === true) {
                    task.destroy();
                } else if (Ext.isArray(task.removed)) {
                    for (var i = 0, len = task.removed.length; i < len; i++) {
                        delete task.requests[task.removed[i]];
                    }
                }
            };
            
            var findByRemoved = function(uri) {
                if (Ext.isArray(task.removed)) {
	                for (var i = 0, len = task.removed.length; i < len; i++) {
	                    if (uri == task.removed[i]) {
	                        return true;
	                    }
	                }
                }
                return false;
            };
            
            for (var uri in requests) {
                if (findByRemoved(uri)) {
                    continue;
                }
                p = {
                    uri: uri,
                    params: (requests[uri].isStore ? requests[uri].options.params : requests[uri].params)
                }
                params.push(p);
            }

            options.params[task.dataProperty] = Ext.encode(params);

            options.success = function(response) {
                if (task && task.isLock()) {
                    return;
                }
                var respObj = Ext.decode(response.responseText);
                var t, req, resp, d;
                for (var i = 0; i < respObj.data.length; i++) {
                    d = respObj.data[i];
                    req = task.requests[d.uri];
                    resp = Ext.apply({}, response);
                    delete d.uri;
                    d.data = ((d.data.charAt(0) == '[' || d.data.charAt(0) == '{') ? Ext.decode(d.data) : d.data);
                    resp.responseText = Ext.encode(d);
                    resp.argument = req.callback.argument || {
                        options: null
                    };
                    t = new Ext.util.DelayedTask(req.callback.success, req.callback.scope, [resp, resp.argument.options]);
                    t.delay(i * 10);
                }
                task.unlock();
                removeFromBuffer();
            };
            options.failure = function(response, e) {
                if (task && task.isLock()) {
                    return;
                }
                var requests = task.requests, t, req, resp, i = 0;
                for (var uri in requests) {
                    req = requests[uri];
                    resp = Ext.apply({}, response);
                    resp.argument = req.callback.argument || {
                        options: null
                    };
                    t = new Ext.util.DelayedTask(req.callback.failure, req.callback.scope, [resp, resp.argument.options]);
                    t.delay(i * 10);
                    i++;
                }
                task.unlock();
                removeFromBuffer();
            };

            callback = {
                success: options.success,
                failure: options.failure,
                scope: task.scope || WINDOW,
                argument: {
                    options: options
                },
                timeout: task.manager.timeout
            };

            Ext.lib.Ajax.request(task.manager.method, options.url, callback, Ext.urlEncode(options.params), options);
        }
    };

    var Runner = new Ext.util.TaskRunner();

    var BaseTask = function(cfg) {
        cfg = cfg || {};
        Ext.apply(this, cfg);
        this.dataProperty = this.dataProperty || 'data';
        this.requests = {}, this.removed = [];
        this.destroyed = locked = false;
    };

    BaseTask.prototype = {
        isLock: function() {
            return this.locked;
        },
        
        lock: function() {
            this.locked = true;
        },

        unlock: function() {
            this.locked = false;
        },

        destroy: function() {
            if (!this.destroyed) {
                delete this.manager.tasks[this.id];
                this.manager = null;
                this.requests = this.removed = null;
                this.onDestroy();
                this.destroyed = true;
            }
        },
        
        onDestroy: function() {}
    };

    var SimpleTask = Ext.extend(BaseTask, {
        execute: function() {
            handleTask(this);
        }
    });

    var PollingTask = function(cfg) {
        PollingTask.superclass.constructor.call(this, cfg);
        this.interval = this.interval || 60000;
        this.thread = {
            run: function() {
                handleTask(this);
            },
            interval: this.interval,
            scope: this
        };
        this.stopped = false;
    };

    Ext.extend(PollingTask, BaseTask, {
        start: function() {
            if (!this.destroyed) {
                this.stopped = false;
                Runner.start(this.thread);
            }
        },

        stop: function() {
            if (!this.destroyed) {
                this.stopped = true;
                Runner.stop(this.thread);
            }
        },

        onDestroy: function() {
            PollingTask.superclass.onDestroy.call(this);
            this.stop();
            this.thread = null;
        }
    });
})();

/**
 * @class Ext.AjaxTask
 * @extends Ext.util.AjaxTaskManager
 * <p>Ajax任务管理器, 合并多个Ajax请求到一个AjaxTask中, 一次请求一次返回</p>
 * <p>使用AjaxTask之前, 需要调用{@link #add}新建一个任务线程.<pre><code>
Ext.AjaxTask.add({
    id: 'newtask', // 任务ID
    url: '/ajaxtask.action' // 请求服务url
});
 * </code></pre></p>
 * 
 * <p>然后和通常一样调用{@link Ext.Ajax#request Ext.Ajax.request()}进行Ajax请求.</p>
 * <p>如果想将页面中的某个Ajax请求加入到AjaxTask中, 只需要在{@link Ext.Ajax#request request}参数中标识出相应的AjaxTask任务ID.<pre><code>
Ext.Ajax.request({
    task: 'newtask', // 任务ID
    url: 'adduser', // 服务端资源
    success: function(response, options) {
        // 当AjaxTask返回服务端响应后, 统一执行回调函数
    }
});
 * </code></pre></p>
 * <p><b>Note</b>: 如果使用同步AJAX请求, 则不会被增加到任务线程中, 同步Ajax请求使用方法参见{@link Ext.Ajax#request request}</p>
 * 
 * <p>{@link Ext.data.Store}同样也可以使用AjaxTask, 只需要在创建Store时, 标识出相应的AjaxTask任务ID.</p>
 * <p><b>proxy</b>必须使用{@link Ext.data.HttpProxy}<pre><code>
var proxy = new Ext.data.HttpProxy({
    api: {
        read: 'loaduser', // 服务端资源
        create: '/cext/user?method=create',
        update: '/cext/user?method=update',
        destroy: '/cext/user?method=remove'
    }
});

var store = new Ext.data.Store({
    task: 'test', // 任务ID
    taskAction: { // 声明哪类请求需要增加到AjaxTask中, 默认只有read请求才加入AjaxTask
        read: true, // True表示加入到AjaxTask中
        create: false, // False表示不加入AjaxTask
        update: false,
        destroy: false,
    },
    proxy: proxy,
    reader: reader,
    writer: writer
});
 * </code></pre></p>
 * 
 * <p>使用AjaxTask, 同时还要在服务端, 提供相应的处理程序, 目前只提供Java支持.</p>
 * 
 * <p>你需要提供一个服务代理类, 例如一个Servlet或Action:<pre><code>
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    response.setContentType("application/json");
    PrintWriter out = response.getWriter();
    String data = request.getParameter("data");
    // AjaxTaskImpl继承需要实现两个接口, parseParams和processRequest
    // parseParams : 解析请求参数
    // processRequest : 处理每个Ajax请求
    AjaxTask<String, String> ajaxTask = new AjaxTaskImpl();
    out.print(ajaxTask.process(data));
}
 * </code></pre></p>
 * <p>详细的使用方法, 请参见<a href="../examples/ajax/ajaxtask.html">Ajax Task Examples</a></p>
 *  
 * <p><b>Note</b>: 需要注意, 只有400的response.status状态码AjaxTask才能被正确处理, 如果AjaxTask请求失败, 那么Task中所有的子Ajax请求都将请求失败.</p>
 * <p>对于不再使用的Ajax任务需要调用{@link #remove}函数将其删除, 特别是轮询任务, 必须从AjaxTask管理器中删除:<pre><code>
var task = Ext.AjaxTask.add({
    id: 'ajaxtaskexample',
    url: 'test/ajaxtask',
    // 创建一个轮询任务, 间隔3秒钟刷新一次
    interval: 3000
});

// 不再使用时, 必须删除
Ext.AjaxTask.remove(task);
 * </code></pre></p>
 * @singleton
 */
Ext.AjaxTask = new Ext.util.AjaxTaskManager({
    /**
     * @cfg {String} method AjaxTask请求HTTP method (defaults to 'POST')
     */
    method: 'POST',

    /**
     * @cfg {String} timeout AjaxTask请求超时时间 (defaults to 30000)
     */
    timeout: 30000,

    /**
     * @cfg {String} header AjaxTask请求头 (defaults to 'ExtPlus AjaxTask')
     */
    header: 'ExtPlus AjaxTask'
});
